package com.yhy.common.core.exception;

import com.yhy.common.core.result.Result;
import com.yhy.common.core.result.ResultEnum;
import com.yhy.common.core.utils.WebUtils;
import com.yomahub.tlog.context.TLogContext;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.web.reactive.WebFluxAutoConfiguration;
import org.springframework.http.HttpStatus;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.validation.BindException;
import org.springframework.web.HttpMediaTypeNotSupportedException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.multipart.MultipartException;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Objects;

/**
 * @author Jax-li
 */
@Slf4j
@RestControllerAdvice
@ConditionalOnMissingBean(WebFluxAutoConfiguration.class)
public class GlobalServletExceptionHandlerResolver {

    public static void warn(HttpServletRequest request, Throwable e, String cause) {
        log.warn("♦♦♦ warn cause:[{}], uri: [{}], traceId: [{}], ex=【{}】",
                cause, request.getRequestURI(), TLogContext.getTraceId(), e.getLocalizedMessage());
    }

    public static void error(HttpServletRequest request, Throwable e, String cause) {
        log.error("♦♦♦ error cause:[{}], uri: [{}], traceId: [{}], ex=【{}】",
                cause, request.getRequestURI(), TLogContext.getTraceId(), e.getLocalizedMessage(), e);
    }

    @ResponseStatus(HttpStatus.METHOD_NOT_ALLOWED)
    @ExceptionHandler({HttpRequestMethodNotSupportedException.class})
    public void requestMethodNotSupported(HttpServletRequest request, HttpServletResponse response, Exception ex) {
        Result<String> result = Result.fail("请求方式不支持: " + request.getMethod());
        warn(request, ex, result.getMessage());
        WebUtils.renderJson(response, result);
    }

    @ResponseStatus(HttpStatus.NOT_ACCEPTABLE)
    @ExceptionHandler({HttpMessageNotReadableException.class, HttpMediaTypeNotSupportedException.class,
            MissingServletRequestParameterException.class})
    public void requestParamNotSupported(HttpServletRequest request, HttpServletResponse response, Exception ex) {
        Result<String> result = Result.fail("请求参数不支持");
        warn(request, ex, result.getMessage());
        result.setData(ex.getLocalizedMessage());
        WebUtils.renderJson(response, result);
    }

    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler({BindException.class})
    public void methodArgumentNotValidExceptionHandler(HttpServletRequest request, HttpServletResponse response, BindException ex) {
        String defaultMessage = ex.getBindingResult().getAllErrors().get(0).getDefaultMessage();
        Result<String> result = Result.fail("参数校验不通过");
        result.setData(defaultMessage);
        warn(request, ex, result.getMessage());
        WebUtils.renderJson(response, result);
    }

    @ResponseStatus(HttpStatus.PAYLOAD_TOO_LARGE)
    @ExceptionHandler({MultipartException.class})
    public void multipartException(HttpServletRequest request, HttpServletResponse response, MultipartException ex) {
        Result<String> result = Result.fail("文件大小超出限制");
        result.setData(Objects.requireNonNull(ex.getRootCause()).getLocalizedMessage());
        warn(request, ex, result.getMessage());
        WebUtils.renderJson(response, result);
    }

    @ResponseStatus(HttpStatus.OK)
    @ExceptionHandler({BizException.class})
    public void bizException(HttpServletRequest request, HttpServletResponse response, BizException ex) {
        Result<String> result = Result.fail(ex);
        error(request, ex, result.getMessage());
        WebUtils.renderJson(response, result);
    }

    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler({IllegalArgumentException.class})
    public void illegalArgumentException(HttpServletRequest request, HttpServletResponse response, IllegalArgumentException ex) {
        Result<String> result = Result.fail(ex.getLocalizedMessage());
        error(request, ex, result.getMessage());
        WebUtils.renderJson(response, result);
    }

    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    @ExceptionHandler({Exception.class, RuntimeException.class})
    public void runtimeException(HttpServletRequest request, HttpServletResponse response, Exception ex) {
        Result<String> result = Result.fail(ResultEnum.INTERNAL_SERVER_ERROR);
        result.setData("运行时异常: " + ex.getLocalizedMessage());
        error(request, ex, ex.getLocalizedMessage());
        WebUtils.renderJson(response, result);
    }

}
